package com.wxl.common.serialport

import android.annotation.SuppressLint
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.lzy.okgo.OkGo
import com.lzy.okgo.callback.FileCallback
import com.lzy.okgo.model.HttpParams
import com.lzy.okgo.model.Response
import com.wxl.common.AppContext
import com.wxl.common.bean.CollectorUpgradeBean
import com.wxl.common.http.CommonHttpUtil
import com.wxl.common.http.OkGoApi
import com.wxl.common.http.SerialHttpCallback
import com.wxl.common.util.FileCommon
import com.wxl.common.util.ObservableUtil
import com.wxl.common.util.SystemUtil
import com.wxl.common.wiget.ProgressUpdatePop
import com.wxl.common.work.CjqVersionWork
import io.reactivex.Observable
import io.reactivex.ObservableOnSubscribe
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Consumer
import io.reactivex.schedulers.Schedulers
import java.io.DataInputStream
import java.io.File
import java.io.FileInputStream
import java.util.concurrent.TimeUnit

class CJQUpdateModel {

    companion object {

        interface OnCjqUpCallback {
            fun onCjqUpEnd()
            fun onCiqUpStart()
        }

        private var mVersion = -1
        private var mCollectorInfoTitle: String? = null
        private var request: OneTimeWorkRequest? = null
        private var mUpgradeAddressNum = 0

        private val m2kDataList = java.util.ArrayList<String>() // 每次读取的2k 数据集合

        private val mCRC16List = java.util.ArrayList<String>() // 每次读取的2k 数据的CRC16集合

        private var dataCount = 0

        private var isUpModeBlock = false//升级被中断

        private var mProgressUpdatePop:ProgressUpdatePop?=null

        open var isPauseReceive = false

        /**
         * 检查采集器版本是否需要升级，需要时自动下载采集器新包
         */
        fun checkNetCjqVersion() {
            val params = HttpParams()
            params.put("mac", SystemUtil.getMac())
            params.put("action", "getCaijiPackage")
            params.put("caijiversion", AppContext.appContext.getCjqVersion())
            OkGoApi.api.get().params(params)
                .execute(object : SerialHttpCallback<CollectorUpgradeBean>("caijiqi_file") {
                    override fun loadDataSuccess(datas: ArrayList<CollectorUpgradeBean>) {
                        if (datas.isNotEmpty()) {
                            val version = datas[0].version!!.toLong()
                            val cjqVersion = AppContext.appContext.getCjqNetVersion()
                            if (version > cjqVersion) {
                                downLoadCJQUpFile(datas[0])
                            } else {
                                queueCjqVersionWork()
                            }
                        } else {
                            queueCjqVersionWork()
                        }
                    }

                    override fun loadDataFailed(e: Throwable) {
                        queueCjqVersionWork()
                    }
                })
        }

        /**
         * 开启轮询检查升级任务
         */
        private fun queueCjqVersionWork() {
            ObservableUtil.delay(60 * 1000, Runnable {
                request = OneTimeWorkRequest.Builder(CjqVersionWork::class.java).build()
                WorkManager.getInstance(AppContext.appContext.getApplication())
                    .beginUniqueWork("checkNetCjqVersion", ExistingWorkPolicy.REPLACE, request!!)
                    .enqueue()
            })
        }


        /**
         * 下载采集器最新包
         */
        private fun downLoadCJQUpFile(model: CollectorUpgradeBean) {
            val url = "http://qbqsp.gstore.cn/uploadfile/caijiqi_software/" + model.filepath
            val cjqFile = FileCommon.getCjqFile()
            if (cjqFile.exists()) {
                cjqFile.delete()
            }
            OkGo.get<File>(url)
                .execute(object : FileCallback(FileCommon.getCjqFileDir().absolutePath, "cjq.bin") {
                    override fun onSuccess(response: Response<File>?) {
                        queueCjqVersionWork()
                        response?.let {
                            if (it.isSuccessful && it.body() != null) {
                                model.version?.let {
                                    AppContext.appContext.saveCjqNetVersion(it.toLong())
                                }
                            }
                        }
                    }

                    override fun onError(response: Response<File>?) {
                        queueCjqVersionWork()
                    }
                })

        }


        /**
         * 停止轮询更新接口
         */
        private fun stopQueueWork() {
            request?.let {
                WorkManager.getInstance(AppContext.appContext.getApplication())
                    .cancelWorkById(it.id)
            }
        }


        /**
         * 接收采集器插入返回信息
         */
        fun checkCjq(callback: OnCjqUpCallback) {

            /**
             * 插入采集器
             */
            SerialD.registerCjqSerial( object : Serial() {
                override fun loadReceiveData(data: String) {
                    //SerialPortManager.cancelIntervalWork()
                    if(!isPauseReceive) {
                        isUpModeBlock = false
                        mProgressUpdatePop?.dismiss()
                        SerialPortManager.send(CmdCode.CJQ_READ_INFO_CMD)//发送获取采集器信息命令
                    }
                }
            })


            Serials.registerStartHexSerial(CmdCode.CJQ_UP_BLOCK_ASK, object : Serial() {
                override fun loadReceiveData(data: String) {
                    //SerialPortManager.cancelIntervalWork()
                    isUpModeBlock = true
                    checkCjq(callback)
                }
            })

            /**
             * 返回采集器信息
             */
            Serials.registerSingleStartHexSerial(CmdCode.CJQ_READ_INFO_ASK, object : Serial() {
                override fun loadReceiveData(data: String) {
                    //val mDetonatorTotalNum = Integer.valueOf(data.substring(12, 16), 16)//雷管数
                    if (data.length > 31) {
                        mVersion = Integer.valueOf(data.substring(28, 32), 16)
                        mCollectorInfoTitle = data.substring(16, 28)
                        val cjqNetVersion = AppContext.appContext.getCjqNetVersion()
                        if (mVersion < cjqNetVersion) {
                            //SerialPortManager.cancelIntervalWork()
                            stopQueueWork()
                            updateCjqBin(callback)
                        } else {
                            callback.onCjqUpEnd()
                        }
                    }
                }
            })

            SerialPortManager.startQueryPortWork()
        }


        /**
         * 升级采集器
         */
        private fun updateCjqBin(callback: OnCjqUpCallback) {
            val cjqFile = FileCommon.getCjqFile()
            if (cjqFile.exists()) {
                callback.onCiqUpStart()
                mProgressUpdatePop = ProgressUpdatePop.show(AppContext.appContext.getCurrentActivity()!!)
                if (!isUpModeBlock) {
                    onInUpdateMode(cjqFile, callback)
                } else {
                    isUpModeBlock = false
                    readUpdateData(cjqFile, callback)
                }
            } else {
                callback.onCjqUpEnd()
            }
        }


        /**
         * step1:进入升级模式
         */
        private fun onInUpdateMode(file: File, callback: OnCjqUpCallback) {
            ProgressUpdatePop.updateProgress(0, "正在进入升级模式，请勿进行其他操作...")
            //loadSerialShotCircuitData()
            Serials.registerSingleStartHexSerial(CmdCode.CJQ_IN_UP_MODE_OK_ASK, object : Serial() {
                override fun loadReceiveData(data: String) {
                    ProgressUpdatePop.updateProgress(10, "进入升级模式成功，请勿进行其他操作...")
                    readUpdateData(file, callback)
                }
            })

            Serials.registerSingleStartHexSerial(CmdCode.CJQ_IN_UP_MODE_E_ASK, object : Serial() {
                override fun loadReceiveData(data: String) {
                    ProgressUpdatePop.updateProgress(-1, "采集器进入升级模式错误...")
                    callback.onCjqUpEnd()
                }
            })

            SerialPortManager.send(CmdCode.CJQ_IN_UP_MODE_CMD)//进入升级模式
        }


        /**
         * step:2  读取升级数据
         */
        private fun readUpdateData(file: File, callback: OnCjqUpCallback) {
            ProgressUpdatePop.updateProgress(20, "正在读取升级文件，请勿进行其他操作...")
            m2kDataList.clear()
            mCRC16List.clear()
            ObservableUtil.doNext(ObservableOnSubscribe<File> {
                DataInputStream(FileInputStream(file)).use { dataStream ->
                    val block = ByteArray(2048)
                    while (dataStream.read(block) != -1) {
                        m2kDataList.add(ByteUtil.bytesToHexString(block))
                        mCRC16List.add(ByteUtil.getCRC_16(block))
                    }
                    dataStream.close()
                    it.onNext(file)
                }
            }, object : ObservableUtil.Companion.OnNextCallback<File>() {
                override fun onNext(t: File) {
                    // SerialPortManager.cancelIntervalWork()
                    ProgressUpdatePop.updateProgress(30, "进入升级模式成功，请勿进行其他操作...")
                    sendDataAddress(callback)
                }

                override fun error(e: Throwable) {
                    e.printStackTrace()
                    Serials.unRegisterNoKeySerial("cjqUpdate")
                    signOutUpMode(true, callback)
                }
            })
        }


        /**
         * step: 3 发送数据地址信息
         */
        private fun sendDataAddress(callback: OnCjqUpCallback) {
            if (mCRC16List.isNotEmpty() && mCRC16List.size > mUpgradeAddressNum) {
                ProgressUpdatePop.updateProgress(
                    30 + mUpgradeAddressNum,
                    "正在写入地址信息，请勿进行其他操作... ${mUpgradeAddressNum + 1}/118"
                )
                val detonatorNum = SerialUtil.getCurrentDetonatorNum(mUpgradeAddressNum)
                Serials.registerSingleStartHexSerial(CmdCode.CJQ_SEND_ADDR_OK_ASK,
                    object : Serial() {
                        override fun loadReceiveData(data: String) {
                            //成功
                            dataCount = 0
                            ProgressUpdatePop.updateProgress(
                                30 + mUpgradeAddressNum,
                                "正在发送数据信息，请勿进行其他操作... ${mUpgradeAddressNum + 1}/118"
                            )
                            send2kData(callback)
                        }
                    })

                Serials.registerSingleStartHexSerial(CmdCode.CJQ_SEND_ADDR_E_ASK,
                    object : Serial() {
                        override fun loadReceiveData(data: String) {
                            //失败，重新发送
                            sendDataAddress(callback)
                        }
                    })

                Serials.registerSingleStartHexSerial(CmdCode.CJQ_SEND_ADDR_JUMP_ASK,
                    object : Serial() {
                        override fun loadReceiveData(data: String) {
                            //跳过
                            checkSendData(callback)
                        }
                    })

                SerialPortManager.send(CmdCode.CJQ_SEND_ADDR_CMD + detonatorNum + CmdCode.CJQ_SEND_ADDR_R_CMD + mCRC16List[mUpgradeAddressNum])
            } else {
                Serials.unRegisterNoKeySerial("cjqUpdate")
                signOutUpMode(true, callback)
            }
        }



        /**
         * step:4  发送2k的数据
         */
        @SuppressLint("CheckResult")
        private fun send2kData(callback: OnCjqUpCallback) {
            if (dataCount == 8) {
                sendWriteData(callback)
                return
            }
            Observable.timer(60, TimeUnit.MILLISECONDS)
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .map {
                    val str = "0" + dataCount + m2kDataList.get(mUpgradeAddressNum)
                        .substring(512 * dataCount, 512 * (dataCount + 1)).toUpperCase()
                    SerialPortManager.sendHexData(str)
                    ++dataCount
                }.subscribe(Consumer {
                    if (it < 9) {
                        send2kData(callback)
                    }
                })
        }


        /**
         * step:5  发送写入命令
         */
        private fun sendWriteData(callback: OnCjqUpCallback) {
            ProgressUpdatePop.updateProgress(
                30 + mUpgradeAddressNum,
                "正在写入数据信息，请勿进行其他操作... ${mUpgradeAddressNum + 1}/118"
            )
            Serials.registerSingleStartHexSerial(CmdCode.CJQ_WRITE_DATA_OK_ASK, object : Serial() {
                override fun loadReceiveData(data: String) {
                    //写入成功
                    checkSendData(callback)
                }
            })

            Serials.registerSingleStartHexSerial(CmdCode.CJQ_WRITE_DATA_E_ASK, object : Serial() {
                override fun loadReceiveData(data: String) {
                    //写入失败,重新写入该地址和数据信息
                    sendDataAddress(callback)
                }
            })

            SerialPortManager.send(CmdCode.CJQ_WRITE_DATA_CMD)
        }

        /**
         * 检查数据是否写入完成
         */
        private fun checkSendData(callback: OnCjqUpCallback) {
            if (mUpgradeAddressNum >= 117) {//完成，退出
                signOutUpMode(false, callback)
            } else {//未完成,循环步骤3
                mUpgradeAddressNum++
                sendDataAddress(callback)
            }
        }


        /**
         * 退出升级模式
         */
        private fun signOutUpMode(isError: Boolean, callback: OnCjqUpCallback) {
            mCRC16List.clear()
            m2kDataList.clear()
            Serials.registerSingleStartHexSerial(CmdCode.CJQ_SIGN_EXIT_OK_ASK,
                object : Serial() {
                    override fun loadReceiveData(data: String) {
                        SerialPortManager.startQueryPortWork()
                        if (!isError) {
                            isUpModeBlock = false
                            val cjqNetVersion = AppContext.appContext.getCjqNetVersion()
                            mCollectorInfoTitle?.let {
                                AppContext.appContext.saveCjqTitle(it)
                            }
                            AppContext.appContext.saveCjqVersion(cjqNetVersion)
                            release()
                            ProgressUpdatePop.updateProgress(150, "升级完成。")
                            CommonHttpUtil.uploadCollectorVersion()
                            stopQueueWork()
                        } else {
                            ProgressUpdatePop.updateProgress(-1, "升级失败。")
                        }
                        callback.onCjqUpEnd()
                    }
                })

            Serials.registerSingleStartHexSerial(CmdCode.CJQ_SIGN_EXIT_E_ASK,
                object : Serial() {
                    override fun loadReceiveData(data: String) {
                        signOutUpMode(isError, callback)
                    }
                })
            val cjqNetVersion = AppContext.appContext.getCjqNetVersion()
            val toString = SerialUtil.getCurrentDetonatorNum(cjqNetVersion).toString()
            SerialPortManager.send(CmdCode.CJQ_SIGN_EXIT_CMD + toString)
        }

        fun exitUpdate(){
            val cjqNetVersion = 35
            val toString = SerialUtil.getCurrentDetonatorNum(cjqNetVersion).toString()
            SerialPortManager.send(CmdCode.CJQ_SIGN_EXIT_CMD + toString)
        }


        fun release() {
            isUpModeBlock = false
            Serials.unRegisterNoKeySerial("cjqUpdate")
            Serials.unRegisterStartHexSerial(CmdCode.CJQ_UP_BLOCK_ASK)
            Serials.unRegisterSingleStartHexSerial(CmdCode.CJQ_IN_UP_MODE_E_ASK)
            Serials.unRegisterSingleStartHexSerial(CmdCode.CJQ_IN_UP_MODE_OK_ASK)
            SerialD.unRegisterCjqSerial()
            Serials.unRegisterSingleStartHexSerial(CmdCode.CJQ_READ_INFO_ASK)
            Serials.unRegisterSingleStartHexSerial(CmdCode.CJQ_SIGN_EXIT_E_ASK)
            Serials.unRegisterSingleStartHexSerial(CmdCode.CJQ_SIGN_EXIT_OK_ASK)
            Serials.unRegisterSingleStartHexSerial(CmdCode.CJQ_SEND_ADDR_OK_ASK)
            Serials.unRegisterSingleStartHexSerial(CmdCode.CJQ_SEND_ADDR_E_ASK)
            Serials.unRegisterSingleStartHexSerial(CmdCode.CJQ_SEND_ADDR_JUMP_ASK)
            Serials.unRegisterSingleStartHexSerial(CmdCode.CJQ_WRITE_DATA_OK_ASK)
            Serials.unRegisterSingleStartHexSerial(CmdCode.CJQ_WRITE_DATA_E_ASK)
        }

    }
}